﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;

namespace SharpShare.Storage.Zip {
    internal static class ZipExtensions {
        private const string PathSeparator = ZipArchiveStorageItem.PathSeparator;

        public static IEnumerable<ZipEntry> ListZipEntries(this ZipFile zipFile) {
            lock (zipFile) {
                var entries = zipFile.Cast<ZipEntry>().ToList();
                return entries;
            }
        }

        public static IEnumerable<string> ListChildPaths(this ZipFile zipFile, string parentPath) {
            if (parentPath == null || !parentPath.EndsWith(PathSeparator)) {
                throw new ArgumentException("Parent path is null, or does not end with the path separator.", "parentPath");
            }

            bool parentIsRoot = (parentPath == PathSeparator);

            foreach (string path in zipFile.ListPaths()) {
                if (parentIsRoot) {
                    // Children in the root cannot have any path separators except for at the very end.
                    if (!path.TrimEnd(PathSeparator[0]).Contains(PathSeparator)) {
                        yield return path;
                    }
                } else {
                    if (path.StartsWith(parentPath)) {
                        string relativePath = path.Remove(0, parentPath.Length);
                        if (relativePath.Length > 0 && !relativePath.TrimEnd(PathSeparator[0]).Contains(PathSeparator)) {
                            yield return path;
                        }
                    }
                }
            }
        }

        public static IEnumerable<string> ListPaths(this ZipFile zipFile) {
            var paths = zipFile.ListPathsRaw()
                .Distinct()
                .OrderBy(p => p);
            return paths;
        }

        private static IEnumerable<string> ListPathsRaw(this ZipFile zipFile) {
            //foreach (ZipEntry entry in zipFile.ListZipEntries()) {
            //    yield return entry.Name;
            //}

            foreach (ZipEntry entry in zipFile.ListZipEntries()) {
                string[] components = entry.Name.Split(new[] { PathSeparator[0] }, StringSplitOptions.RemoveEmptyEntries);

                for (int x = 0; x < components.Length; x++) {
                    string currentPath = string.Join(PathSeparator, components, 0, x + 1);
                    if (x < (components.Length - 1)) {
                        currentPath += PathSeparator;
                    } else {
                        if (entry.Name.EndsWith(PathSeparator)) {
                            currentPath += PathSeparator;
                        }
                    }

                    yield return currentPath;
                }
            }
        }

        public static void Update(this ZipFile zipFile, Action updater) {
            lock (zipFile) {
                zipFile.BeginUpdate();
                try {
                    updater();
                } catch {
                    zipFile.AbortUpdate();
                    throw;
                }
                zipFile.CommitUpdate();
            }
        }
    }

    internal sealed class ZipEntryStaticDataSource : IStaticDataSource {
        public ZipEntryStaticDataSource(ZipFile zipFile, ZipEntry zipEntry) {
            this.ZipFile = zipFile;
            this.ZipEntry = zipEntry;
        }

        public ZipFile ZipFile { get; private set; }
        public ZipEntry ZipEntry { get; private set; }

        #region IStaticDataSource Members

        public System.IO.Stream GetSource() {
            Stream stream = this.ZipFile.GetInputStream(this.ZipEntry);
            return stream;
        }

        #endregion
    }

}
